""" S3D.PathUtils package. Contains usefull path utils based on S3D.Wildcard """
import os
import stat
import shutil
import re
from S3D.Wildcard import Wildcard
from S3D.Parser import Parser
from S3D.Tokenizer import tokenize, Token
from unittest import TestCase

__all__ = ["walk", "copy"]

def walk(path, recursively = False, walkFiles = True, walker = os.walk):
    root, pattern = os.path.split(path)
    w = Wildcard(pattern)

    for path, dirs, files in walker(root):
        names = walkFiles and files or dirs

        for m in w.filter(names):
            yield path, m

        if not recursively:
            break

_norm_path = lambda x: os.path.normcase(os.path.abspath(x))
            
def copy(src, dst, recursively = False, forceOverwrite = False, ignoreSubdirs = False, verbose = False):
    src_root, _             = os.path.split(src)
    dst_root, dst_pattern   = os.path.split(dst)  

    src_root = _norm_path(src_root)

    for path, name in walk(src, recursively):
        path    = _norm_path(path)
        sub_dir = path[len(src_root) + 1:]

        if not ignoreSubdirs:
            cur_root = os.path.join(dst_root, sub_dir)

        dst_res = os.path.join(cur_root, dst_pattern and name.rename(dst_pattern) or name.source)
        dst_res = name.rename(dst_res)

        try:
            os.makedirs(os.path.split(dst_res)[0])
        except:
            pass

        src = os.path.join(path, name.source)
        dst = dst_res
        if not shutil._samefile(src, dst):
            if verbose:
                print("%s -> %s" % (src, dst))

            if forceOverwrite and os.access(dst, os.F_OK):
                os.chmod(dst, stat.S_IREAD | stat.S_IWRITE)

            shutil.copy(src, dst)

def forceDelete(path):
    # make sure that file exists
    if not os.access(dst, os.F_OK):
        return

    # remove readonly flag
    os.chmod(path, stat.S_IREAD | stat.S_IWRITE)

    # delete file
    os.remove(path)

class PrintCache:
    __slots__ = ("cache")

    def __init__(self):
        self.cache = set()

    def print(self, text):
        if text in self.cache:
            return

        self.cache.add(text)
        print(text)

class _SubstringTranslator(Parser):
    def parse(self):
        res = []

        isFirst = True
        prevIsSymbol = False

        part = 1

        while not self.nextHasType("eof"):
            if self.nextHasType("word", "int", "float"):
                if not isFirst:
                    res.append(prevIsSymbol and r"\s*" or r"\s+")
                isFirst = False
                prevIsSymbol = False

                res.append(self.getNextValue())
                self.matchAny()
                continue

            if self.tryMatchSymbol('<'):
                name = self.matchWord()
                self.matchSymbol('>')

                if not isFirst:
                    res.append(prevIsSymbol and r"\s*" or r"\s+")
                isFirst = False
                prevIsSymbol = False

                res.append(r")(?P<%s>[^\s" % name)
                if not self.nextHasType("eof"):
                    res.append(re.escape(self.getNextValue()[0]))
                res.append(r"]+)(?P<part%d>" % part)
                part += 1
                continue

            if self.nextIsSymbol():
                if not isFirst:
                    res.append(r"\s*")
                isFirst = False
                prevIsSymbol = True

                res.append(re.escape(self.getNextValue()))
                self.matchAny()
                continue

            self.matchNothing()

        return r"(?P<part0>(^|\s)%s(\s|$))" % "".join(res)

def translateSubstring(pattern):
    """
        Translates user substring pattern to regexp.
        Pattern examples:
            a = <value>             # translated to (^|\s)a\s*=\s*(?P<value>[^\s]+)(\s|$)
            a b c                   # translated to (^|\s)a\s+b\s+c(\s|$)
            <a>=<b>                 # translated to (^|\s)(?P<a>[^\s\=]+)\s*=\s*(?P<b>[^\s]+)(\s|$)
    """

    tr = _SubstringTranslator()
    res = tr.compile(pattern)
    if res is None:
        raise SyntaxError(tr.errorMsg)

    return res

class _Tests(TestCase):
    def test(self):
        def check(pattern, result):
            r = translateSubstring(pattern)
            self.assertEqual(r, result)

        check("parentDesc = <id>",  r"(?P<part0>(^|\s)parentDesc\s*\=\s*)(?P<id>[^\s]+)(?P<part1>(\s|$))")
        check("<a> + <b> = 10",     r"(?P<part0>(^|\s))(?P<a>[^\s\+]+)(?P<part1>\s*\+\s*)(?P<b>[^\s\=]+)(?P<part2>\s*\=\s*10(\s|$))")
